package org.yaac.server.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.yaac.client.service.StatisticsService;
import org.yaac.server.util.DatastoreUtil;
import org.yaac.server.util.GWTUtil;
import org.yaac.shared.stat.StatConstants;
import org.yaac.shared.stat.StatDTO;
import org.yaac.shared.stat.StatDTO.KindWrapper;
import org.yaac.shared.stat.models.Kind;
import org.yaac.shared.stat.models.KindIsRootEntity;
import org.yaac.shared.stat.models.KindNonRootEntity;
import org.yaac.shared.stat.models.NSKind;
import org.yaac.shared.stat.models.NSKindIsRootEntity;
import org.yaac.shared.stat.models.NSKindNonRootEntity;
import org.yaac.shared.stat.models.NSPropertyTypePropertyNameKind;
import org.yaac.shared.stat.models.Total;

import com.google.appengine.api.NamespaceManager;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Query;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;

/**
 * @author Max Zhu (thebbsky@gmail.com)
 *
 */
public class StatisticsServiceImpl implements StatisticsService {

	@Override
	public StatDTO loadStat() {		
		// step 1 : kick off all async queries
		// namespace query : we use low-level API rather than Objectify to manage namespace is because 
    	Iterable<Entity> namespaceEntities = 
    		DatastoreServiceFactory.getAsyncDatastoreService().prepare(new Query(StatConstants.STAT_NAMESPACE)).asIterable();
		
		Objectify ofy  = ObjectifyService.begin();

    	Iterable<Total> totals = ofy.query(Total.class).fetch();	// total stat query
    	
    	// use HashMap here because Guava's map implementations don't support empty string
    	Map<String, Iterable<NSKind>> nsKindsMap = new HashMap<String, Iterable<NSKind>>();
    	Map<String, Iterable<NSKindIsRootEntity>> nsRootKindsMap = new HashMap<String, Iterable<NSKindIsRootEntity>>();
    	Map<String, Iterable<NSKindNonRootEntity>> nsNonRootKindsMap = new HashMap<String, Iterable<NSKindNonRootEntity>>();
    	Map<String, Iterable<NSPropertyTypePropertyNameKind>> nsPropertyTypeNameKindsMap = 
    		new HashMap<String, Iterable<NSPropertyTypePropertyNameKind>>();
    	
    	// kick off per namespace queries for kind / property
    	for (Entity entity : namespaceEntities) {
    		String namespace = entity.getKey().getName();
    		
    		if (namespace == null) {
    			namespace = "";	// for empty namespace, the key will be 1, so namespace will be null here
    		}
    		
    		NamespaceManager.set(namespace);
    		
    		// __Stat_Ns_Kind__ query
    		nsKindsMap.put(namespace, ofy.query(NSKind.class).fetch());
    		
    		// __Stat_Ns_Kind_IsRootEntity__ query
    		nsRootKindsMap.put(namespace, ofy.query(NSKindIsRootEntity.class).fetch());
    		
    		// __Stat_Ns_Kind_NotRootEntity__ query
    		nsNonRootKindsMap.put(namespace, ofy.query(NSKindNonRootEntity.class).fetch());
    		
    		// __Stat_Ns_PropertyType_PropertyName_Kind__ query
    		nsPropertyTypeNameKindsMap.put(namespace, ofy.query(NSPropertyTypePropertyNameKind.class).fetch());
    	}
    	NamespaceManager.set(null);	// reset namespace
    	
    	// ====================== step 2, assemble results=====================
    	Total total = DatastoreUtil.singleEntityFrom(totals);
    	
    	Map<String, Iterable<KindWrapper>> nsKindsWrapperMap = new HashMap<String, Iterable<KindWrapper>>();
    	
    	for (String namespace : nsKindsMap.keySet()) {
    		// kinds
    		Map<String, KindWrapper> kindWrapperMap = new HashMap<String, KindWrapper>();
    		for (Kind kind : nsKindsMap.get(namespace)) {
    			KindWrapper kindWrapper = new KindWrapper();
    			kindWrapper.setKind(kind);
    			kindWrapperMap.put(kind.getKind_name(), kindWrapper);
    		}
    		
    		for (KindIsRootEntity kindRoot : nsRootKindsMap.get(namespace)) {
    			kindWrapperMap.get(kindRoot.getKind_name()).setRootKind(kindRoot);
    		}
    		
    		for (KindNonRootEntity kindNonRoot : nsNonRootKindsMap.get(namespace)) {
    			kindWrapperMap.get(kindNonRoot.getKind_name()).setNonRootKind(kindNonRoot);
    		}
    		
    		nsKindsWrapperMap.put(namespace, new ArrayList<KindWrapper>(kindWrapperMap.values()));
    	}
		
    	Map<String, Iterable<NSPropertyTypePropertyNameKind>> nsPropertyMap = 
    		new HashMap<String, Iterable<NSPropertyTypePropertyNameKind>>();
    	
    	for (String namespace : nsPropertyTypeNameKindsMap.keySet()) {
    		nsPropertyMap.put(namespace, GWTUtil.ensureGWTCompatible(nsPropertyTypeNameKindsMap.get(namespace)));
    	}
    	
		return new StatDTO(total, nsKindsWrapperMap, nsPropertyMap);
	}
}
